/*************************************************************
 * Title: AuthorizationCheckInterceptor.java
 * Description: 
 * Author: Huang Shaobin
 * Email: shaobin.software@gmail.com
 * CreateTime: Feb 21, 2013 2:56:59 PM
 ************************************************************/
package snow.core.security;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.util.Assert;

import snow.core.context.AccessContext;
import snow.core.domain.Result;
import snow.core.exceptions.BusinessException;
import snow.core.web.soap.ServiceBeanResponseSupport;

/**
 * 权限校验拦截器
 */
public class AuthorizationCheckInterceptor implements MethodInterceptor, InitializingBean {

	private final Log log = LogFactory.getLog(getClass());

	private MessageSource messageSource;
	private SecurityService securityService;
	private PrincipalService principalService;

	public void afterPropertiesSet() throws Exception {
		Assert.notNull(securityService, "The property [securityService] must not be null!");
	}

	protected Locale getLocale() {
		AccessContext context = AccessContext.getCurrent();
		List<Locale> locales = context.getLocalesFromRequest();
		final Locale locale = CollectionUtils.isNotEmpty(locales) ? locales.get(0) : Locale.getDefault();
		return locale;
	}

	public MessageSource getMessageSource() {
		return this.messageSource;
	}

	public PrincipalService getPrincipalService() {
		return principalService;
	}

	public SecurityService getSecurityService() {
		return securityService;
	}

	/**
	 * @param invocation
	 * @return
	 * @throws Throwable
	 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
	 */
	public Object invoke(MethodInvocation invocation) throws Throwable {
		log.info("开始执行权限校验……");
		final Object target = invocation.getThis();
		final Method method = invocation.getMethod();
		final Class<?> returnType = method.getReturnType();
		final Permission permission = securityService.searchPermissionFrom(target, method);
		String code = "200000";
		String message = "failure.unknown.business.exception";
		if (permission != null && permission.isNeedCheck()) {
			Principal principal = lookuPrincipal(); // 获取权限载体信息
			if (principal == null) { // 判断是否登录超时
				message = messageSource.getMessage("failure.security.user.login.timeout", ArrayUtils.EMPTY_OBJECT_ARRAY, getLocale());
				if (ServiceBeanResponseSupport.class.isAssignableFrom(returnType)) {
					return returnType.getConstructor(new Class<?>[] { boolean.class, String.class, String.class }).newInstance(false, "200001", message);
				}
				return new Result(false, "200201", message);
			} else if (!principal.hasPermission(permission)) { // 判断是权限载体是否被授权访问该资源
				message = messageSource.getMessage("failure.security.user.unauthorized", ArrayUtils.EMPTY_OBJECT_ARRAY, getLocale());
				if (ServiceBeanResponseSupport.class.isAssignableFrom(returnType)) {
					return returnType.getConstructor(new Class<?>[] { boolean.class, String.class, String.class }).newInstance(false, "200001", message);
				}
				return new Result(false, "200001", message);
			}
		}
		log.info("结束执行权限校验！");
		try {
			return invocation.proceed(); // 执行处理操作
		} catch (Exception e) {
			if (e instanceof BusinessException) {
				BusinessException be = (BusinessException) e;
				code = be.getCode();
				message = messageSource.getMessage(be.getMessage(), ArrayUtils.EMPTY_OBJECT_ARRAY, getLocale());
			}
			if (ServiceBeanResponseSupport.class.isAssignableFrom(returnType)) {
				return returnType.getConstructor(new Class<?>[] { boolean.class, String.class, String.class }).newInstance(false, code, message);
			}
			return new Result(false, code, message);
		}
	}

	protected Principal lookuPrincipal() throws BusinessException {
		final AccessContext context = AccessContext.getCurrent();
		final Serializable sessionCurrentUserId = context.getFromSession("current_user_id", Serializable.class);
		return principalService.searchPrincipalFrom(sessionCurrentUserId);
	}

	public void setMessageSource(MessageSource messageSource) {
		this.messageSource = messageSource;
	}

	public void setPrincipalService(PrincipalService principalService) {
		this.principalService = principalService;
	}

	public void setSecurityService(SecurityService securityService) {
		this.securityService = securityService;
	}

}
